<!--
 * @Description: 流程设计界面
 * @Author: zhailei
 * @Date: 2021-01-18 15:17:26
 * @LastEditTime: 2021-09-02 16:51:34
 * @FilePath: \vite-demoe:\wwwroot\Vue\\-cloud-vue\src\components\TopVUI\bpmn\bpmnDesign.vue
-->
<template>
  <div class="main">
    <div class="bpmn-viewer">
      <!-- 头部操作按钮 -->
      <vue-header
        ref="header"
        class="bpmn-viewer-header"
        :processData="initData"
        :modeler="bpmnModeler"
        @save="saveData"
        :bpmnClose="bpmnClose"
        @restart="restart"
        @handleExportSvg="handleExportSvg"
        @handleExportBpmn="handleExportBpmn"
        @processSave="processSave"
        @fitViewport="fitViewport"
        @openBpmn="openBpmn"
        @processSimulation="processSimulation"
        @processVerify="processVerify"
        @close="
          () => {
            $emit('close');
          }
        "
      ></vue-header>
      <div class="bpmn-viewer-container">
        <div class="bpmn-viewer-content" ref="bpmnViewer"></div>
      </div>
    </div>
    <!-- 流程设计面板 -->
    <bpmn-panel
      ref="bpmnPanel"
      v-if="bpmnModeler"
      :modeler="bpmnModeler"
      :process="initData"
      :isEdit="isEdit"
      @updateProcesName="updateProcesName"
    ></bpmn-panel>
  </div>
</template>

<script>
import templateXml from "./data/template";
import BpmnModeler from "bpmn-js/lib/Modeler";
// import customTranslate from "./data/translate/customTranslate";
import customControlsModule from "./customControls";
import customTranslate from "./customTranslate/customTranslate";
import VueHeader from "./Header";
// import BpmnPanel from "./panel/designPanel";
import BpmnPanel from "./panel/designPanel";
import activitiModele from "./data/activiti.json";
import flowableModdle from "./data/flowable.json";
import BpmData from "./data/BpmData";
import "./assets/css/vue-bmpn.scss";
import "./assets/css/font-awesome.min.css";
// 模拟流转流程
import tokenSimulation from "bpmn-js-token-simulation";
// 流程校验
import processLint from "bpmn-js-bpmnlint";
import processLintConfig from "./helper/.bpmnlintrc";

export default {
  name: "VueBpmn",
  data() {
    return {
      bpmnModeler: null,
      initTemplate: "",
      initData: {},
      zoom: 1,
    };
  },
  props: {
    product: String,
    bpmnClose: Boolean,
    isEdit: {
      type: Boolean,
    },
  },
  components: {
    VueHeader,
    BpmnPanel,
  },
  mounted() {
    this.getInitData();
    this.init();
  },
  methods: {
    updateProcesName(e) {
      this.initData.name = e;
    },
    // 初始化模板
    getInitData() {
      let processId = new Date().getTime();
      this.initTemplate = templateXml.initTemplate(processId);
      this.initData = {
        id: "process" + processId,
        key: "process" + processId,
        name: "流程" + processId,
        xml: this.initTemplate,
        batchApproval: "0", // 是否开启批量审批 0：关闭 1：开启
        ifMultiForm: "0", // 是否开启多表单 0：关闭 1：开启
      };
    },
    // 初始化流程界面
    init() {
      // 支持activiti和flowable
      let _moddleExtensions = this.getModdleExtensions();
      // 获取画布 element
      this.canvas = this.$refs.bpmnViewer;

      // 创建Bpmn对象
      var customTranslateModule = {
        translate: ["value", customTranslate],
      };
      this.bpmnModeler = new BpmnModeler({
        container: this.canvas,
        linting: { bpmnlint: processLintConfig },
        additionalModules: [
          customTranslateModule,
          customControlsModule,
          tokenSimulation,
          processLint,
        ],
        moddleExtensions: _moddleExtensions,
      });
      // 初始化建模器内容
      this.initDiagram(this.initTemplate);
      this.adjustPalette(); //自定义
      // this.setNodeColor();
      this.setPosition();
    },
    setPosition() {
      const bbox = document.querySelector(".main .viewport").getBBox();
      const currentViewbox = this.bpmnModeler.get("canvas").viewbox();
      const elementMid = {
        x: bbox.x + bbox.width / 2 - 65,
        y: bbox.y,
      };
      this.bpmnModeler.get("canvas").viewbox({
        x: elementMid.x,
        y: elementMid.y,
        width: currentViewbox.width,
        height: currentViewbox.height,
      });
    },
    setNodeColor() {
      let that = this;
      let nodeData = ["StartEvent_01ydzqe", "Activity_06zwtmp"];
      const elementRegistry = that.bpmnModeler.get("elementRegistry");
      let modeling = that.bpmnModeler.get("modeling");
      nodeData.forEach((e) => {
        setTimeout(() => {
          if (elementRegistry._elements[e]) {
            modeling.setColor(elementRegistry._elements[e].element, {
              stroke: "#009688",
            });
          }
        }, 500);
      });
    },
    initDiagram(xml) {
      this.bpmnModeler.importXML(xml, (err) => {
        if (err) {
          // this.$Message.error("打开模型出错,请确认该模型符合Bpmn2.0规范");
        } else {
        }
      });
    },
    /* 编辑时流程数据设置*/
    initDiagramAgain(
      actDeModelKey,
      actDeModelName,
      xml,
      permissionType,
      permissionValue,
      batchApproval,
      ifMultiForm
    ) {
      let _tag = document.getElementsByClassName(
        "djs-direct-editing-parent"
      )[0];
      if (_tag) {
        _tag.style.display = "none";
      }
      if (actDeModelKey) {
        // this.initData = {
        this.initData.id = actDeModelKey;
        this.initData.key = actDeModelKey;
        this.initData.name = actDeModelName;
        this.initData.xml = xml;
        if (batchApproval) this.initData.batchApproval = batchApproval;
        if (ifMultiForm) this.initData.ifMultiForm = ifMultiForm;
        // };
      } else {
        this.getInitData();
      }
      this.$refs.bpmnPanel.setStarterData(permissionType, permissionValue);
      this.bpmnModeler.importXML(this.initData.xml, (err) => {
        if (err) {
          this.adjustPalette();
          this.$message.error("打开模型出错,请确认该模型符合Bpmn2.0规范");
        } else {
          this.adjustPalette();
        }
        this.addModelerListener();
      });
    },
    /* 模型监听*/
    addModelerListener() {
      // 监听 modeler
      const bpmnjs = this.bpmnModeler;
      const that = this;
      // 'shape.removed', 'connect.end', 'connect.move'
      const events = ["shape.added", "shape.move.end", "shape.removed"];
      events.forEach(function(event) {
        that.bpmnModeler.on(event, (e) => {
          var elementRegistry = bpmnjs.get("elementRegistry");
          var shape = e.element ? elementRegistry.get(e.element.id) : e.shape;
          if (event === "shape.added") {
            // 展示新增图形的属性
          } else if (event === "shape.move.end") {
            // 展示新增图形的属性
          } else if (event === "shape.removed") {
            // // 展示默认的属性
          }
        });
      });
    },
    /* 导入*/
    openBpmn(file) {
      const reader = new FileReader();
      reader.readAsText(file, "utf-8");
      reader.onload = () => {
        this.createNewDiagram(reader.result);
      };
      return false;
    },
    async createNewDiagram(data) {
      // 将字符串转换成图显示出来
      // xml = xml.replace(/</g, '&lt;')
      // data = data.replace(/>/g, '&gt;')
      // data = data.replace(/<!\[CDATA\[(.+)]]>/g, "&lt;![CDATA[$1]]&gt;");
      data = data.replace(/ < /g, " <![CDATA[<]]> ");
      data = data.replace(/ & /g, " <![CDATA[&]]> ");
      try {
        await this.bpmnModeler.importXML(data);
        // this.fillColor()
      } catch (err) {
        console.error(err.message, err.warnings);
        this.$message.warn("无效的BPMN 2.0图文件");
      }
    },
    getProcessElement() {
      return this.bpmnModeler.getDefinitions().rootElements[0];
    },
    // 下载
    downloadFile(filename, data, type) {
      var a = document.createElement("a");
      var url = window.URL.createObjectURL(new Blob([data], { type: type }));
      a.href = url;
      a.download = filename;
      a.click();
      window.URL.revokeObjectURL(url);
    },
    // 导出XML
    async handleExportBpmn() {
      try {
        let { xml } = await this.bpmnModeler.saveXML({ format: true });
        // xml = xml.replace(/&lt;/g, "<");
        // xml = xml.replace(/&gt;/g, ">");
        xml = xml.replace(/ < /g, " <![CDATA[<]]> ");
        xml = xml.replace(/ & /g, " <![CDATA[&]]> ");
        this.downloadFile(
          `${this.getProcessElement().name}.xml`,
          xml,
          "application/xml"
        );
        return xml;
      } catch (err) {
        console.log(err);
      }
    },
    // 导出SVG
    async handleExportSvg() {
      try {
        const { svg } = await this.bpmnModeler.saveSVG({ format: true });
        this.downloadFile(this.getProcessElement().name, svg, "image/svg+xml");
        return svg;
      } catch (err) {
        console.log(err);
      }
    },
    // 让图能自适应屏幕
    fitViewport() {
      this.zoom = this.bpmnModeler.get("canvas").zoom("fit-viewport");
      const bbox = document.querySelector(".main .viewport").getBBox();
      const currentViewbox = this.bpmnModeler.get("canvas").viewbox();
      const elementMid = {
        x: bbox.x + bbox.width / 2 - 65,
        y: bbox.y + bbox.height / 2,
      };
      this.bpmnModeler.get("canvas").viewbox({
        x: elementMid.x - currentViewbox.width / 2,
        y: elementMid.y - currentViewbox.height / 2,
        width: currentViewbox.width,
        height: currentViewbox.height,
      });
      this.zoom = (bbox.width / currentViewbox.width) * 1.8;
    },
    setEncoded(type, data) {
      const encodedData = encodeURIComponent(data);
      if (data) {
        if (type === "XML") {
          return {
            filename: "diagram.bpmn20.xml",
            href: "data:application/bpmn20-xml;charset=UTF-8," + encodedData,
            data: data,
          };
        }
        if (type === "BPMN") {
          return {
            filename: "diagram.bpmn",
            href: "data:application/bpmn20-xml;charset=UTF-8," + encodedData,
            data: data,
          };
        }
        if (type === "SVG") {
          this.initData.svg = data;
          return {
            filename: "diagram.svg",
            href: "data:application/text/xml;charset=UTF-8," + encodedData,
            data: data,
          };
        }
      }
    },
    // 保存时获取XML数据
    saveData(data) {
      let formData = this.$refs.bpmnPanel.getFormData();
      if (
        formData.data.userType === "candidateUsers" &&
        !formData.data.candidateUsers
      ) {
        // 设置审批人：指定人员
        return this.$message.error("请设置审批人：指定人员！");
      } else if (
        formData.data.userType === "candidateGroups" &&
        !formData.data.candidateGroups
      ) {
        // 设置审批人：指定角色
        return this.$message.error("请设置审批人：指定角色！");
      } else if (
        formData.data.userType === "department" &&
        !formData.data.candidateGroups
      ) {
        // 设置审批人：指定岗位
        return this.$message.error("请设置审批人：指定岗位！");
      }
      let parameters = {
        processId: this.initData.id,
        processKey: this.initData.key,
        processName: this.initData.name,
        batchApproval: this.initData.batchApproval, // 批量审批
        ifMultiForm: this.initData.ifMultiForm, // 是否多表单
        description: formData.description,
        xml: data,
        formBtnList: formData["formBtnList"],
        timeoutList: formData.timeoutList,
        actMyNodeFormFieldList: formData.actMyNodeFormFieldList,
        actMyNodeFormList: formData.actMyNodeFormList,
        formFieldList: formData.formFieldList,
        formNoticeList: formData.formNoticeList,
        permissionType: formData.permissionType,
        permissionValue: formData.permissionValue,
        formModuleType: formData.formModuleType,
        sign: formData.sign,
      };
      this.$emit("bpmSave", parameters);
    },
    //保存
    save() {
      this.$refs.header.deploy();
    },
    // 获取表单数据
    getFormLists(e) {
      this.formList = e;
      this.$refs.bpmnPanel.getFormList(this.formList);
    },
    // 获取流程数据
    processSave(data) {
      let initData = this.initData;
      data.procId = initData.key;
      data.name = initData.name;
      this.$emit("processSave", data);
    },
    // 新建
    restart() {
      let _tag = document.getElementsByClassName(
        "djs-direct-editing-parent"
      )[0];
      if (_tag) {
        _tag.style.display = "none";
      }
      let processId = new Date().getTime();
      this.initTemplate = templateXml.initTemplate(processId);
      this.initData = {
        id: "process" + processId,
        key: "process" + processId,
        name: "流程" + processId,
        xml: this.initTemplate,
        batchApproval: "0",
        ifMultiForm: "0",
      };
      this.initDiagram(this.initTemplate);
    },
    // 流程属性
    getModdleExtensions() {
      let moddleExtensions = {};
      if (this.product == "flowable") {
        moddleExtensions = {
          flowable: flowableModdle,
        };
      }
      if (this.product == "activiti") {
        moddleExtensions = {
          activiti: activitiModele,
        };
      }
      return moddleExtensions;
    },
    // 流转模拟
    processSimulation() {
      this.bpmnModeler.get("toggleMode").toggleMode();
      this.adjustPalette();
    },
    // 流程校验
    processVerify() {
      this.bpmnModeler.get("linting").toggle();
    },
    // 左侧工具栏
    adjustPalette() {
      try {
        // 获取 bpmn 设计器实例
        const djsPalette = this.canvas.children[0].children[1].children[10];
        const palette = djsPalette.children[0];
        const allGroups = palette.children;
        // allGroups[0].style["display"] = "none";
        allGroups[4].style["display"] = "none";
        allGroups[5].style["display"] = "none";
        // allGroups[6].style["display"] = "none";
        allGroups[7].style["display"] = "none";
      } catch (e) {
        console.log(e);
      }
    },
  },
};
</script>

<style>
@import "./assets/css/bpmn-font.css";
@import "./assets/css/bpmn-visiable.css";
</style>

<style lang="scss" scoped>
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
@import "~bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css";
@import "./assets/css/vue-bmpn.scss";
@import "./assets/css/font-awesome.min.css";
.main {
  display: flex;
  height: 100vh;
}
/deep/ .djs-container .djs-palette {
  // display: block !important;
}
/deep/ .djs-context-pad {
  display: block !important;
}

/* /deep/ .djs-visual rect,
/deep/ .djs-visual circle,
/deep/ .djs-visual path,
/deep/ .djs-visual polygon{
	stroke: #1890ff !important
} */

/* /deep/ .djs-visual rect{
	x: 0;
	y: 20;
	width: 100 !important;
	height: 40 !important;
	rx: 20;
	ry: 20;
} */

// 隐藏空白任务节点
/deep/.bpmn-icon-task {
  display: none !important;
}

// 左侧工具栏显示文字
/deep/.djs-palette {
  border-radius: 10px;
  box-shadow: 4px 4px 10px #cccccc;
}
/deep/.djs-palette.open {
  width: 140px !important;
}
/deep/.djs-palette-entries [class*=" bpmn-icon-"]:before {
  line-height: 46px !important;
}
/deep/.djs-palette-entries .entry {
  float: none !important;
  cursor: pointer;
  @each $text, $content, $color in (bpmn-icon-hand-tool, "抓手", #167cf3),
    (bpmn-icon-lasso-tool, "批量选择", #167cf3),
    (bpmn-icon-space-tool, "空间工具", #167cf3),
    (bpmn-icon-connection-multi, "连接线", #167cf3),
    (bpmn-icon-start-event-none, "开始事件", #3fd574),
    (bpmn-icon-intermediate-event-none, "中间/边界事件", #167cf3),
    (bpmn-icon-end-event-none, "结束事件", #f96666),
    (bpmn-icon-gateway-none, "条件判断", #fbaf3e),
    (bpmn-icon-subprocess-expanded, "创建子流程", #167cf3),
    (bpmn-icon-user-task, "流程", #167cf3),
    // (bpmn-icon-call-activity, '调用活动', #167cf3),
      (bpmn-icon-participant, "创建泳池", #167cf3)
  {
    &.#{$text} {
      &::before {
        color: $color;
      }
      &::after {
        content: $content;
        font-size: 14px;
        position: absolute;
        left: 45px;
        white-space: nowrap;
        color: #8c8484;
        height: 46px;
        line-height: 46px;
      }
      &:hover::after {
        color: #40a9ff;
      }
    }
  }
}

/deep/.entry {
  @each $text, $color in (bpmn-icon-connection-multi, #167cf3),
    (bpmn-icon-start-event-none, #3fd574),
    (bpmn-icon-intermediate-event-none, #167cf3),
    (bpmn-icon-end-event-none, #f96666), (bpmn-icon-gateway-none, #fbaf3e),
    (bpmn-icon-user-task, #167cf3), (bpmn-icon-trash, #ff5b5b)
  {
    &.#{$text} {
      &::before {
        color: $color;
      }
    }
  }
}
</style>
